/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/
// consts
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
// FP
const isFulFilled = (status) => status === FULFILLED;
const isRejected = (status) => status === REJECTED;
const setImmediately = (func) => setTimeout(func, 0);
class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }
  // Promise的状态仅可更改一次
  status = PENDING;
  // 成功后使用的data
  data = undefined;
  // 失败后使用的变量存储
  reason = undefined;
  // 存储成功回调队列
  onResolveQueue = [];
  // 存储失败回调队列
  onRejectedQueue = [];
  resolve = (data) => {
    if (data instanceof MyPromise) {
      return data.then(resolve, reject);
    }
    if (this.status === PENDING) {
      this.status = FULFILLED;
      this.data = data;
      while (this.onResolveQueue.length) {
        this.onResolveQueue.shift()();
      }
    }
    // do nothing
  };
  reject = (reason) => {
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.reason = reason;
      while (this.onRejectedQueue.length) {
        this.onRejectedQueue.shift()();
      }
    }
  };
  then(onResolved, onRejected) {
    onResolved =
      typeof onResolved === "function" ? onResolved : (value) => value;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (error) => {
            throw error;
          };
    // 整合then链式调用的返回格式
    const returnPromise = new MyPromise((resolve, reject) => {
      if (isFulFilled(this.status)) {
        setImmediately(() => {
          try {
            const nextVal = onResolved(this.data);
            resolvePromise(returnPromise, nextVal, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      } else if (isRejected(this.status)) {
        setImmediately(() => {
          try {
            const nextVal = onRejected(this.reason);
            resolvePromise(returnPromise, nextVal, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      } else {
        // 处理异步调用情况 例如XHR、setTimeout
        onResolved &&
          this.onResolveQueue.push(() => {
            setImmediately(() => {
              try {
                const nextVal = onResolved(this.data);
                resolvePromise(returnPromise, nextVal, resolve, reject);
              } catch (e) {
                reject(e);
              }
            });
          });
        onRejected &&
          this.onRejectedQueue.push(() => {
            setImmediately(() => {
              try {
                const nextVal = onRejected(this.reason);
                resolvePromise(returnPromise, nextVal, resolve, reject);
              } catch (e) {
                reject(e);
              }
            });
          });
      }
    });
    return returnPromise;
  }

  catch(onRejected) {
    return this.then(undefined, onRejected);
  }
  finally(callback) {
    return this.then(
      (value) => {
        return MyPromise.resolve(callback()).then(() => value);
      },
      (reason) => {
        return MyPromise.resolve(callback()).then(() => {
          throw reason;
        });
      }
    );
  }
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      const result = [];
      let index = 0;
      function addResult(res, i) {
        result[i] = res;
        index++;
        if (index === promises.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < promises.length; i++) {
        const promise = promises[i];
        if (promise instanceof MyPromise) {
          promise.then(
            (res) => addResult(res, i),
            (error) => {
              reject(error);
            }
          );
        } else {
          addResult(promise, i);
        }
      }
    });
  }
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        const promise = promises[i];
        if (promise instanceof MyPromise) {
          promise.then(
            (res) => resolve(res),
            (error) => reject(error)
          );
        }
      }
    });
  }
  static resolve(value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise((resolve, reject) => resolve(value));
  }
  static reject(error) {
    return new MyPromise((_, reject) => reject(error));
  }
}

function resolvePromise(promsie2, x, resolve, reject) {
  var then;
  var thenCalledOrThrow = false;
  if (promsie2 === x) {
    return reject(
      new TypeError("Chaining cycle detected for promise #<Promise>")
    );
  }
  if (x instanceof MyPromise) {
    if (x.status === PENDING) {
      x.then(function (v) {
        resolvePromise(promise2, v, resolve, reject);
      }, reject);
    } else {
      x.then(resolve, reject);
    }
    return;
    // promise 对象
    // x.then(value => resolve(value), reason => reject(reason));
    // x.then(resolve, reject);
  }
  // handleThenable
  if (x !== null && (typeof x === "object" || typeof x === "function")) {
    try {
      then = x.then;
      if (typeof then === "function") {
        then.call(
          x,
          function rs(y) {
            if (thenCalledOrThrow) return;
            thenCalledOrThrow = true;
            return resolvePromise(promise2, y, resolve, reject);
          },
          function rj(r) {
            if (thenCalledOrThrow) return;
            thenCalledOrThrow = true;
            return reject(r);
          }
        );
      } else {
        resolve(x);
      }
    } catch (e) {
      if (thenCalledOrThrow) return;
      thenCalledOrThrow = true;
      return reject(e);
    }
  } else {
    resolve(x);
  }
}

MyPromise.deferred = MyPromise.defer = function () {
  var dfd = {};
  dfd.promise = new MyPromise(function (resolve, reject) {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};

module.exports = MyPromise;
